FLPY_SECTORS_PER_TRACK equ 18
app_lba_start equ 99
FDC_DOR         equ 0x3F2  ; 数字输出寄存器
FDC_MSR         equ 0x3F4  ; 主状态寄存器
FDC_FIFO        equ 0x3F5  ; 数据寄存器

; mov eax,cs
; mov ss,eax
; mov sp,0x7c00

;计算 GDT 所在逻辑段的地址
mov eax,[cs:pgdt+0x7c00+0x02] ;不加 2 是长度，加 2 是 pgdt对应的物理地址
xor edx,edx
mov ebx,16
div ebx

mov ds,eax  ; eax是段地址
mov ebx,edx ; edx是偏移地址

; 省略 0x00 和 0x04
mov dword [ebx+0x08],0x0000_ffff
mov dword [ebx+0x0c],0x00cf_9200

mov dword [ebx+0x10],0x7c00_01ff
mov dword [ebx+0x14],0x0040_9800

;创建以上代码段的别名描述符(这是一个数据段)
mov dword [ebx+0x18],0x7c0001ff    ;基地址为0x00007c00，512字节
mov dword [ebx+0x1c],0x00409200    ;粒度为1个字节，数据段描述符

mov dword [ebx+0x20],0x7c00fffe
mov dword [ebx+0x24],0x00cf9600

;建立保护模式下的显示缓冲区描述符   
mov dword [ebx+0x28],0x80007fff    ;基地址为0x000B8000，界限0x07FFF 
mov dword [ebx+0x2c],0x0040920b    ;粒度为字节

; 初始化GDTR
mov word [cs:pgdt+0x7c00],47   ;GDT的界限=total bytes-1
lgdt [cs:pgdt+0x7c00]  ; load GDT

in al,0x92
or al,0000_0010B
out 0x92,al     ;通过南桥芯片ICH打开A20地址线

cli   ;保护模式下中断机制与实模式不同，没有建立新的中断机制之前，可以先禁止中断

mov eax,cr0
or eax,1   ; 将cr0的0位设为1，标志着进入保护模式
mov cr0,eax

; 从这里开始进入保护模式
jmp dword 0x0010:flush   ; 这里的0008是段选择子

[bits 32]
flush:
    mov eax, 0x28
    mov es,eax
    xor eax,eax
    mov word [es:eax], 0x0744
    mov word [es:eax+2], 0x0745
    mov word [es:eax+4], 0x0742
    mov word [es:eax+6], 0x0741
    mov word [es:eax+8], 0x0747

    mov eax,0x18
    mov ds,eax
    
    ;先读取程序头部
    xor di,di
    mov si,app_lba_start
    call read_floppy_disk_0
    hlt

;================
; 读1个扇区的软盘数据到内存
; di、si - 扇区 LBA 值，使用di的低12位和si的全部16位，共计28位
; ds:bx - 写入内存逻辑地址
;================
read_floppy_disk_0:
    push ax
    push bx
    push cx
    push dx
    push ds
    push es

    ; 再计算柱面号 
    mov cx,FLPY_SECTORS_PER_TRACK*2
    mov ax,si
    mov dx,di
    div cx
    mov [@3],al

    ; 再计算磁头号
    mov cx,FLPY_SECTORS_PER_TRACK*2
    mov ax,si
    mov dx,di
    div cx
    mov ax,dx
    xor dx,dx
    mov cx,FLPY_SECTORS_PER_TRACK
    div cx
    mov [@3+1],al

    ; 计算扇区号
    mov cx,18
    mov ax,si
    mov dx,di
    div cx
    inc dx
    mov [@3+2],dl

    ; 设置 DMA
    call setup_dma
    ; 重置软盘控制器
    call reset_floppy
    ; 读取数据
    call read_sector

    pop es
    pop ds
    pop dx
    pop cx
    pop bx
    pop ax
    ret

setup_dma:
    ; 1. 禁用 DMA 通道 2
    mov dx, 0x0A       ; DMA 主屏蔽寄存器端口
    mov al, 0x04       ; 禁用通道 2
    out dx, al

    ; 2. 设置 DMA 地址寄存器
    mov dx, 0x04       ; 通道 2 的地址寄存器
    mov ax, [floppy_buffer]     ; 数据缓冲区偏移地址（低 16 位）
    out dx, al         ; 输出低 8 位
    mov al, ah         ; 高 8 位
    out dx, al

    ; 3. 设置 DMA 页寄存器
    mov dx, 0x81       ; 通道 2 的页寄存器
    mov ax, [floppy_buffer+2] ; 数据缓冲区段地址（高 8 位段地址）      
    out dx, al

    ; 4. 设置 DMA 传输长度（计数寄存器）
    mov dx, 0x05       ; 通道 2 的计数寄存器
    mov ax, 511        ; 传输字节数减 1（512 - 1 = 511）
    out dx, al         ; 输出低 8 位
    mov al, ah         ; 高 8 位
    out dx, al

    ; 5. 设置 DMA 模式寄存器
    mov dx, 0x0B       ; DMA 模式寄存器
    mov al, 0x56       ; 通道 2，读模式，单字节传输
    out dx, al

    ; 6. 启用 DMA 通道 2
    mov dx, 0x0A       ; DMA 主屏蔽寄存器
    mov al, 0x02       ; 启用通道 2
    out dx, al

; 该设置floppy了
reset_floppy:
    ; 发送复位命令
    mov dx, FDC_DOR
    mov al, 0x00       ; 复位软盘控制器
    out dx, al
    mov al, 0x1C       ; 重新启用软盘控制器和驱动器 0
    out dx, al

        ; 等待复位完成
    call wait_floppy_ready
    ret

    ; 等待软盘控制器就绪
    wait_floppy_ready:
        mov dx, FDC_MSR
    .wait:
        in al, dx
        test al, 0x80      ; 检查主状态寄存器的忙位（第 7 位）
        jz .wait          ; 如果忙，继续等待
        ret

; ============================
; 3. 读取软盘扇区
; ============================
read_sector:
    ; 发送 READ DATA 命令到软盘控制器
    mov dx, FDC_FIFO
    mov al, 0xE6       ; 命令：读数据
    out dx, al
    mov al, [@3+1]       ; 起始头号
    shl al, 2
    or  al, 0x00
    out dx, al
    mov al, [@3]       ; 起始磁道号
    out dx, al
    mov al, [@3+1]       ; 起始头号
    out dx, al
    mov al, [@3+2]       ; 起始扇区号（第2个扇区）
    out dx, al
    mov al, 0x02       ; 每扇区 512 字节
    out dx, al
    mov al, [@3+2]
    inc al
    out dx, al
    mov al, 0x1B       ; GAP3 长度
    out dx, al
    mov al, 0xFF       ; 数据长度（无关）
    out dx, al

    ; 等待操作完成
    call wait_floppy_ready

    ret 

floppy_buffer: dd 0x10000 ; 用于存放读取的扇区数据
pgdt             dw 0
                dd 0x00007e00      ;GDT的物理地址
@3: db 0,0,0    ; 用于存储 柱面、磁头、扇区
times 510-($-$$) db 0
db 0x55,0xaa